package assignment0.parser;

import assignment0.node.AssignNode;
import assignment0.node.ExpressionNode;
import assignment0.node.FactorNode;
import assignment0.node.IdentifierNode;
import assignment0.node.Node;
import assignment0.node.NumberNode;
import assignment0.node.TermNode;
import assignment0.tokenizer.Token;
import assignment0.tokenizer.Tokenizer;

public class ParserImpl implements Parser {

	private Tokenizer tokenizer;

	public ParserImpl(Tokenizer tInput) {
		this.tokenizer = tInput;
	}

	@Override
	public Node parse() {
		AssignNode result = new AssignNode();
		Token t1 = tokenizer.next();
		Token t2 = tokenizer.next();

		if (t1.type().equals(Token.Type.IDENTIFIER) && t2.type().equals(Token.Type.EQ)) {
			IdentifierNode identifierNode = new IdentifierNode();
			identifierNode.value = t1.text();

			result.left = identifierNode;
			result.right = parseExpression();
		} else {
			throw new RuntimeException("Incorrect beginning of statement");
		}
		return result;
	}

	private Node parseExpression() {
		ExpressionNode result = new ExpressionNode();
		result.left = parseTerm();

		Token next = tokenizer.peek();
		
		if (next != null && (next.type().equals(Token.Type.PLUS) || next.type().equals(Token.Type.MINUS))) {
			if (next.type().equals(Token.Type.PLUS)) {
				result.operator = "+";
			} else {
				result.operator = "-";
			}
			tokenizer.next();
			// In my opinion parseTerm should be correct
			// however as the examples show that parseExpression is called again
			// this is also done here
			// result.right = parseTerm();
			result.right = parseExpression();
		} else if (next != null && next.type().equals(Token.Type.RIGHT_PAREN)) {
			tokenizer.next();
			next = tokenizer.peek();
		}
		
		return result;
	}

	private Node parseTerm() {
		TermNode result = new TermNode();
		result.left = parseFactor();

		Token next = tokenizer.peek();
		if (next != null && (next.type().equals(Token.Type.DIV) || next.type().equals(Token.Type.MULT))) {
			if (next.type().equals(Token.Type.DIV)) {
				result.operator = "/";
			} else {
				result.operator = "*";
			}
			tokenizer.next();
			// In my opinion parseFactor should be correct
			// however as the examples show that parseExpression is called again
			// this is also done here
			// result.right = parseFactor();
			result.right = parseTerm();
		}
		return result;
	}

	private Node parseFactor() {
		FactorNode result = new FactorNode();
		Token t1 = tokenizer.next();
		if (t1.type().equals(Token.Type.NUMBER)) {
			NumberNode node = new NumberNode();
			node.value = (Integer) t1.value();
			result.node = node;
		} else {
			result.node = parseExpression();
		}
		return result;
	}
}
